
#include "xCores.h"

using namespace x2lib::xCores;

#pragma region Mutex

Mutex::Mutex()
{
#ifdef __X2LIB_WIN32__
	m_mutex = (int)CreateMutex(NULL, FALSE, NULL);
#elif defined __X2LIB_LINUX__
	if (pthread_mutex_init(&m_mutex, NULL)<0)
	{
		perror("pthread_mutex_init");
	}
#endif
}

Mutex::~Mutex()
{
#ifdef __X2LIB_WIN32__
	CloseHandle((HANDLE)m_mutex);
#elif defined __X2LIB_LINUX__
	pthread_mutex_destroy(&m_mutex);
#endif
}

bool Mutex::Lock() const
{
#ifdef __X2LIB_WIN32__
	return WAIT_OBJECT_0 == WaitForSingleObject((HANDLE)m_mutex, INFINITE);
#elif defined __X2LIB_LINUX__
	return 0 == pthread_mutex_lock(&m_mutex);
#endif
}

bool Mutex::Unlock() const
{
#ifdef __X2LIB_WIN32__
	return 0 != ReleaseMutex((HANDLE)m_mutex);
#elif defined __X2LIB_LINUX__
	return 0 == pthread_mutex_unlock(&m_mutex);
#endif
}

#pragma endregion


#if 0
#pragma region Signal

Signal::Signal(int iInit, int iMax, char* pName)
{
    m_signal = 0;

    if (pName && pName[0])
        sprintf(m_szName, pName);
    else
        sprintf(m_szName, "xCores::Signal_%08d_%08d", (int)this, (int)&m_signal);

    m_iInit = iInit;
    m_iMax = iMax;
    Reset(m_iInit, m_iMax);
}
Signal::~Signal()
{
    if (m_signal)
    {
#ifdef __X2LIB_WIN32__
        CloseHandle((HANDLE)m_signal);
#elif defined(__X2LIB_LINUX__)
        union semun sem_union;
        semctl(m_signal, 0, IPC_RMID, sem_union);
#endif
    }
}
void Signal::Reset()
{
    Reset(m_iInit, m_iMax);
}
void Signal::Reset(int iInit, int iMax)
{
    if (m_signal)
    {
#ifdef __X2LIB_WIN32__
        CloseHandle((HANDLE)m_signal);
#elif defined(__X2LIB_LINUX__)
        union semun sem_union;
        if (semctl(m_signal, 0, IPC_RMID, sem_union))
            printf("Failed to delete semaphore\n");
#endif
    }

    m_iInit = iInit;
    m_iMax = iMax;
#ifdef __X2LIB_WIN32__
    m_signal = (int)::CreateSemaphoreA(NULL, iInit, iMax, m_szName);
#if 0
    m_signal = (int)::CreateEvent(NULL, FALSE, FALSE, NULL);
#endif
#elif defined(__X2LIB_LINUX__)
    for (int i = 0; i < 255; i++)
    {
        m_signal = semget(ftok(".", i), 1, 0666 | IPC_CREAT | IPC_EXCL);
        if (-1 != m_signal)
            break;
    }
    if (m_signal == -1)
    {
        perror("semget failed!\n");
        return;
    }
    union semun sem_union;
    sem_union.val = 0; // 初始化为0，表示首次调用Wait会阻塞，1表示首次调用Wait不阻塞
    if (semctl(m_signal, 0, SETVAL, sem_union))
    {
        perror("semctl error!\n");
    }
#endif	
}

bool Signal::Wait(uint64_t uWaitms) const
{
#ifdef __X2LIB_WIN32__
    return WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_signal, uWaitms);
#elif defined(__X2LIB_LINUX__)
    struct sembuf sem_b = { 0/*信号量编号*/, -1/*V操作*/, SEM_UNDO };
    if (semop(m_signal, &sem_b, 1) == -1)
    {
        printf("semaphore_p failed\n");
        return 0;
    }
    return 1;
#endif
}

bool Signal::Notify(int nCnt) const
{
#ifdef __X2LIB_WIN32__
    return 0 != ::ReleaseSemaphore((HANDLE)m_signal, nCnt, NULL);
#if 0
    return 0 != ::SetEvent((HANDLE)m_signal);
#endif
#elif defined(__X2LIB_LINUX__)
    struct sembuf sem_b = { 0/*信号量编号*/, 1/*V操作*/, SEM_UNDO };
    if (semop(m_signal, &sem_b, 1) == -1)
    {
        printf("semaphore_v failed\n");
        return 0;
    }
    return 1;
#endif
}

#pragma endregion
#endif

#pragma region Engine

Engine::Info::Info() { Reset(); }
Engine::Info::~Info() { }
void Engine::Info::Reset()
{
	n_tid = 0;
	handle = nullptr;
	// signal.Reset();
	state = EState_Spare;
	vData = nullptr;
	pThis = nullptr;
}

Engine::Engine(uint16_t nMax)
{
	m_nInfos = nMax;
	m_pInfos = new Info[m_nInfos];
};
Engine::~Engine()
{
	delete[] m_pInfos;
}
uint32_t Engine::Ignite(void* vData)
{
	Info* pInfo = getFreeInfo();
	do
	{
		if (pInfo == nullptr) { break; }

		pInfo->vData = vData;
		pInfo->pThis = this;
		pInfo->handle = new std::thread(Engine::threadProc, pInfo);
		if (pInfo->handle == nullptr)
		{
			pInfo->Reset();
			delete pInfo->handle;
			pInfo->handle = NULL;
			break;
		}
		std::thread::id tid = ((std::thread*)pInfo->handle)->get_id();
		if (sizeof(tid) == sizeof(uint64_t))
		{
			pInfo->n_tid = *(uint64_t*)&tid;
		}
		else
		{
			pInfo->n_tid = *(uint32_t*)&tid;
		}
		return pInfo->n_tid;
	} while (0);

	return 0;
}
bool Engine::Stall(uint32_t id, uint32_t uWiatms)
{
	Info* pInfo = getInfoById(id);
	if (pInfo == nullptr) { return false; }

	onEngineExit(pInfo->n_tid, pInfo->vData);

	if (pInfo->signal.Acquire(1, uWiatms))
	{
#ifdef __X2LIB_WIN32__
		TerminateThread((HANDLE)pInfo->handle, -1);
#elif defined __X2LIB_LINUX__
		pthread_cancel(pInfo->n_tid);
#endif
		pInfo->Reset();
		return true;
	}

	return false;
}
void Engine::Suspend(uint32_t id)
{
	Info* pInfo = getInfoById(id);
	if (pInfo != nullptr)
	{
#ifdef __X2LIB_WIN32__
		pInfo->state = EState_Pause;
		SuspendThread((HANDLE)pInfo->handle);
#elif defined __X2LIB_LINUX__
#endif
	}
}
void Engine::Resume(uint32_t id)
{
	Info* pInfo = getInfoById(id);
	if (pInfo != nullptr)
	{
#ifdef __X2LIB_WIN32__
		pInfo->state = EState_Start;
		ResumeThread((HANDLE)pInfo->handle);
#elif defined __X2LIB_LINUX__
#endif
	}
}
Engine::EState Engine::GetState(uint32_t id)
{
	Info* pInfo = getInfoById(id);
	if (pInfo != nullptr)
	{
		return pInfo->state;
	}
	return EState_Spare;
}
uint32_t Engine::GetCurId()
{
#ifdef __X2LIB_WIN32__
	// 虽然GetCurrentThreadId返回0，但直接给0值好像会导致PostThreadMessage异常
	return GetCurrentThreadId();
#elif defined __X2LIB_LINUX__
	return pthread_self();
#endif  
};

bool Engine::startTimer(uint32_t timerId, uint32_t msPeriod, void (*timerProc)(uint32_t, void*), void* vData)
{
    if (m_mapTimer.find(timerId) != m_mapTimer.end()) { return false; }
    std::function<void()> func = [this, timerProc, msPeriod, timerId, vData]
    {
		auto& timer = m_mapTimer[timerId];
		std::mutex mutex;
		std::unique_lock<std::mutex> lock(mutex);
		while (std::get<0>(timer))
		{
			std::get<1>(timer)->wait_for(lock, std::chrono::milliseconds(msPeriod));
			if (std::get<0>(timer)) {
				timerProc(timerId, vData);
			}
		}

    };
    m_mapTimer[timerId] = std::tuple<bool, std::condition_variable*, std::thread*>(true, new std::condition_variable(), new std::thread(func));
    return true;
}

void Engine::stopTimer(uint32_t timerId)
{
    auto timer = m_mapTimer.find(timerId);
    if (timer == m_mapTimer.end()) { return; }
	std::get<0>(timer->second) = false;
	std::get<1>(timer->second)->notify_one();
	if (std::get<2>(timer->second)->joinable())
	{
		std::get<2>(timer->second)->join();
	}
	delete std::get<2>(timer->second);
    m_mapTimer.erase(timerId);
}

// 忽略EState_Spare和EState_Locked
Engine::Info* Engine::getInfoById(uint32_t id)
{
	for (int i = 0; i < m_nInfos; i++)
	{
		Info* pInfo = &m_pInfos[i];
		if (pInfo->state != EState_Spare && pInfo->state != EState_Locked && pInfo->n_tid == id)
		{
			return pInfo;
		}
	}
	return nullptr;
}
Engine::Info* Engine::getFreeInfo()
{
	for (int i = 0; i < m_nInfos; i++)
	{
		Info* pInfo = &m_pInfos[i];
		if (pInfo->state == EState_Spare)
		{
			pInfo->Reset();
			pInfo->state = EState_Locked;
			return pInfo;
		}
	}
	return nullptr;
}
void* Engine::threadProc(void* p)
{
	Info* pInfo = (Info*)p;
	pInfo->state = EState_Start;
	pInfo->pThis->onEngineExec(pInfo->n_tid, pInfo->vData);
	pInfo->signal.Release(1);
	pInfo->state = EState_Spare;
	return (void*)0;
}

#pragma endregion
